--[[
    完全取代 os 中时间相关操作的模块
    映射了 os.time, os.date 和 os.difftime
    并且增加了时间计算的便捷方法，这些方法的规则：

    -- 计算距离 base(可以是时间戳，也可以是date表) 之后|之前指定 value(单位: hour, day, ...) 时间的时间戳，
    -- base: 可以是时间戳，也可以是date表
    -- value: 单位: hour, day, ...
    -- flag: nil(获取常规取值, 注意: _month 函数目前没有支持这个选项，因为每个月的天数不相同),
    --      's'(获取指定 *(hour, day, ...) 的开始时间), 'e'(获取指定 * 的结束时间)
        after*(base, value, flag): number
        before*(base, value, flag): number

    -- 计算从当前时间开始，指定 *(hour, day, ...) 时间的时间戳，flag 同上
        next*(value, flag): number
        last*(value, flag): number

    -- 特殊函数
        tomorrow(flag): number
        yesterday(flag): number

    -- 判断是否同样的 *(hour, day, ...) 的便捷函数
        isSame*(time1, time2): bool
]]
local skynet = require "skynet"

local ostime = os.time
local osdate = os.date
local osdifftime = os.difftime
local type = type

local SECOND = 1
local MINUTE = 60 * SECOND
local HOUR = 60 * MINUTE
local DAY = 24 * HOUR
local WEEK = 7 * DAY

local FIRST_WDAY = skynet.getenvn("first_wday") or 1

local M = {
    SECOND = SECOND,
    MINUTE = MINUTE,
    HOUR = HOUR,
    DAY = DAY,
    WEEK = WEEK,
    FIRST_WDAY = FIRST_WDAY
}

function M.time(tbl)
    return ostime(tbl)
end

function M.now()
    return ostime()
end

function M.date(format, time)
    return osdate(format, time)
end

function M.diff(time1, time2)
    if type(time1) == "table" then
        time1 = ostime(time1)
    end

    if type(time2) == "table" then
        time2 = ostime(time2)
    end
    return osdifftime(time1, time2)
end

local function calcTime(base, value, unit, flag)
    local basetime = base
    if type(base) == "table" then
        basetime = ostime(base)
    end

    local time = basetime + value * unit
    if not flag then
        return time
    end

    assert(flag == 's' or flag == 'e', "flag must be 's' or 'e'")
    local date = osdate("*t", time)
    date.sec = 0
    local start_time
    if unit == HOUR then
        date.min = 0
    elseif unit == DAY then
        date.min = 0
        date.hour = 0
    elseif unit == WEEK then
        date.min = 0
        date.hour = 0
        date.day = date.day - ((date.wday - FIRST_WDAY + 7) % 7)
    end

    start_time = ostime(date)
    if flag == 's' then
        return start_time
    end

    return start_time + unit - 1
end

-- min
function M.afterMin(base, min, flag)
    return calcTime(base, min, MINUTE, flag)
end

function M.beforeMin(base, min, flag)
    return calcTime(base, -min, MINUTE, flag)
end

function M.nextMin(min, flag)
    return calcTime(ostime(), min, MINUTE, flag)
end

function M.lastMin(min, flag)
    return calcTime(ostime(), -min, MINUTE, flag)
end

-- hour
function M.afterHour(base, hour, flag)
    return calcTime(base, hour, HOUR, flag)
end

function M.beforeHour(base, hour, flag)
    return calcTime(base, -hour, HOUR, flag)
end

function M.nextHour(hour, flag)
    return calcTime(ostime(), hour, HOUR, flag)
end

function M.lastHour(hour, flag)
    return calcTime(ostime(), -hour, HOUR, flag)
end

-- day
function M.afterDay(base, day, flag)
    return calcTime(base, day, DAY, flag)
end

function M.beforeDay(base, day, flag)
    return calcTime(base, -day, DAY, flag)
end

function M.next_Day(day, flag)
    return calcTime(ostime(), day, DAY, flag)
end

function M.lastDay(day, flag)
    return calcTime(ostime(), -day, DAY, flag)
end

function M.tomorrow(flag)
    return calcTime(ostime(), 1, DAY, flag)
end

function M.yesterday(flag)
    return calcTime(ostime(), -1, DAY, flag)
end

-- week
function M.afterWeek(base, week, flag)
    return calcTime(base, week, WEEK, flag)
end

function M.beforeWeek(time, week, flag)
    return calcTime(time, -week, WEEK, flag)
end

function M.nextWeek(week, flag)
    return calcTime(ostime(), week, WEEK, flag)
end

function M.lastWeek(week, flag)
    return calcTime(ostime(), -week, WEEK, flag)
end

-- month, 每个月的天数是不一样的，所以只给出获取月开始时间和结束时间
function M.afterMonth(base, month, flag)
    assert(flag == 's' or flag == 'e', "flag must be 's' or 'e'")
    local basedate = type(base) == "table" and base or osdate("*t", base)
    if flag == 's' then
        return ostime({
            year = basedate.year,
            month = basedate.month + month,
            day = 1,
            hour = 0
        })
    else
        return ostime({
            year = basedate.year,
            month = basedate.month + month + 1,
            day = 1,
            hour = 0
        }) - 1
    end
end

function M.beforeMonth(time, month, flag)
    return M.afterMonth(time, -month, flag)
end

function M.nextMonth(month, flag)
    return M.afterMonth(ostime(), month, flag)
end

function M.lastMonth(month, flag)
    return M.beforeMonth(ostime(), month, flag)
end

-- 距离min后分钟差多久
function M.untilMin(min, flag)
    return M.diff(M.nextMin(min, flag), M.now())
end

function M.untilHour(hour, flag)
    return M.diff(M.nextHour(hour, flag), M.now())
end

function M.untilDay(day, flag)
    return M.diff(M.next_Day(day, flag), M.now())
end

function M.untilWeek(week, flag)
    return M.diff(M.nextWeek(week, flag), M.now())
end

-- same_*
function M.isSameDay(time1, time2)
    local date1, date2
    if type(time1) == "number" then
        date1 = osdate("*t", time1)
    end

    if type(time2) == "number" then
        date2 = osdate("*t", time2)
    end

    return date1.year == date2.year and date1.month == date2.month and date1.day == date2.day
end

function M.isSameMonth(time1, time2)
    local date1, date2
    if type(time1) == "number" then
        date1 = osdate("*t", time1)
    end

    if type(time2) == "number" then
        date2 = osdate("*t", time2)
    end

    return date1.year == date2.year and date1.month == date2.month
end

function M.isSameWeek(time1, time2)
    return M.afterWeek(time1, 0, 's') == M.afterWeek(time2, 0, 's')
end

return M
